package com.jl.proxy;

import com.jl.EmptyUtils;
import com.jl.ReflectUtils;
import com.jl.Tuple;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReaderFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 代理注册
 */
@Configuration
public class BeanScanner implements BeanDefinitionRegistryPostProcessor, ResourceLoaderAware, ApplicationContextAware {

    private MetadataReaderFactory metadataReaderFactory;

    private ResourcePatternResolver resourcePatternResolver;

    private ApplicationContext applicationContext;

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) {
        // 获取启动类所在包
        List<String> packagesList = AutoConfigurationPackages.get(applicationContext);
        List<String> packages = Arrays.asList(packagesList.get(0), "com.jl");
        // 获取所有接口 && 获取所有代理实现类
        List<Class<?>> superclass = new ArrayList<>();
        List<Class<?>> proxyClass = new ArrayList<>();
        packages.forEach(packageStr -> {
            // 获取所有接口
            List<Class<?>> superclas = ReflectUtils.ClassReflect.getPackageClassByInterface(packageStr);
            superclass.addAll(superclas);
            // 获取所有代理实现类
            List<Class<?>> proxyClasss = ReflectUtils.ClassReflect.getProxyClass(packageStr, InvocationHandlerImpl.class);
            proxyClass.addAll(proxyClasss);
        });
        if (!EmptyUtils.check(superclass) || !EmptyUtils.check(proxyClass)) {
            return;
        }
        // 获取代理实现类实现的接口 v1=代理实现类 v2=代理实现类实现的接口
        List<Tuple.Tuple2<Class<?>, Class<?>>> list = new ArrayList<>();
        proxyClass.forEach(beanClazz -> {
            List<Class<?>> anInterface = ReflectUtils.ClassReflect.getInterface(beanClazz);
            list.add(new Tuple.Tuple2<>(beanClazz, anInterface.get(0)));
        });
        if (!EmptyUtils.check(list)) {
            return;
        }
        //循环所有接口
        superclass.forEach(beanClazz -> list.forEach(tuple2 -> {
            //判断是否是继承 代理实现类实现的接口
            boolean instanceOf = ReflectUtils.ClassReflect.isInstanceOf(tuple2.getV2(), beanClazz);
            if (!instanceOf) {
                return;
            }
            // BeanDefinition构建器
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
            GenericBeanDefinition definition = (GenericBeanDefinition) builder.getRawBeanDefinition();
            //在这里，我们可以给该对象的属性注入对应的实例。
            //definition.getConstructorArgumentValues().addGenericArgumentValue(beanClazz);
            // 如果构造函数中不止一个值,可以使用这个api,指定是第几个参数
            definition.getConstructorArgumentValues().addIndexedArgumentValue(0, beanClazz);
            definition.getConstructorArgumentValues().addIndexedArgumentValue(1, tuple2.getV1());
            // 定义Bean工程(最终会用上面add的构造函数参数值作为参数调用JLFactoryBean的构造方法)
            definition.setBeanClass(FactoryBeanImpl.class);
            //这里采用的是byType方式注入，类似的还有byName等
            definition.setAutowireMode(GenericBeanDefinition.AUTOWIRE_BY_TYPE);
            String simpleName = beanClazz.getSimpleName();
            // 首字母小写注入容器
            simpleName = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
            beanDefinitionRegistry.registerBeanDefinition(simpleName, definition);
        }));
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
        this.metadataReaderFactory = new CachingMetadataReaderFactory(resourceLoader);
    }
}
